package com.wallet.my.util;

import io.reactivex.Flowable;
import io.reactivex.disposables.Disposable;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.HDUtils;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.bitcoinj.wallet.UnreadableWalletException;
import org.reactivestreams.Subscription;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.Response;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.tx.ChainId;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;
import java.security.SecureRandom;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

/**
 * eth交易用到的工具类
 **/
@Component
public class EthUtil {

    private static SecureRandom secureRandom;

    //    查询当前节点高度
    public long getMyBlockNumber(Web3j web3j) {
        EthBlockNumber ethBlockNumber = null;
        try {
            ethBlockNumber = web3j.ethBlockNumber().send();
        } catch (IOException e) {
            e.printStackTrace();
        }
        long blockNumber = ethBlockNumber.getBlockNumber().longValue();
        return blockNumber;
    }

    //    根据address查询nonce交易次数
    public BigInteger getNonce(Web3j web3j, String address) {
        EthGetTransactionCount getNonce = null;
        try {
            getNonce = web3j.ethGetTransactionCount(address, DefaultBlockParameterName.PENDING).send();
        } catch (IOException e) {
            e.printStackTrace();
        }
        BigInteger transactionCount = getNonce.getTransactionCount();
        return transactionCount;
    }

    //    根据address查询余额
    public BigDecimal getMyBalance(Web3j web3j, String address) {
        EthGetBalance ethGetBalance = null;
        try {
            ethGetBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();    //发起请求查询余额
        } catch (IOException e) {
            e.printStackTrace();
        }
        BigDecimal bigDecimal = new BigDecimal(ethGetBalance.getBalance());           //查询余额，单位是wei
        BigDecimal balance = Convert.fromWei(bigDecimal, Convert.Unit.ETHER);         //格式化单位
        return balance;
    }

    //查询gasprice,   一般是1Gwei=1,000,000,000wei   10的9次方
    public BigInteger getMyGasPrice(Web3j web3j) {
        EthGasPrice ethGasPrice = null;
        try {
            ethGasPrice = web3j.ethGasPrice().send();
        } catch (IOException e) {
            e.printStackTrace();
        }
        BigInteger gasPrice = ethGasPrice.getGasPrice();
        return gasPrice;
    }

    //    估算gas消耗量，一般默认gasLimit写21000gas升*1Gwei= 21000 Gwei = 0.000021 eth
    public BigInteger getEstimateGasLimit(Web3j web3j, Transaction transaction) {
        EthEstimateGas ethEstimateGas = null;
        try {
            ethEstimateGas = web3j.ethEstimateGas(transaction).send();
        } catch (IOException e) {
            e.printStackTrace();
        }
        BigInteger estimateGasLimit = ethEstimateGas.getAmountUsed();
        return estimateGasLimit;
    }

    //判断余额是否充足
    public boolean balanceIsEnough(BigDecimal mybalance, BigInteger value, BigInteger estimateGasLimit, BigInteger gasPrice) {
        BigInteger charge = estimateGasLimit.multiply(gasPrice);    //乘法算出手续费，wei单位
        BigInteger sum = value.add(charge);                        //转账金额+手续费，算出本次花费
        BigInteger balanceToWei = Convert.toWei(mybalance, Convert.Unit.ETHER).toBigInteger();  //余额转成wei单位
        if (balanceToWei.compareTo(sum) < 0) {
            return false;
        } else {
            return true;
        }
    }

    //    对交易进行签名并发送交易
    public String signAndSend(Web3j web3j, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String toAddr, BigInteger value, String data, String privateKey) {
        String transHash = "";
        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, toAddr, value, data);  //创建交易信息
        Credentials credentials = Credentials.create(privateKey);                                                         //获取交易凭证
        byte[] signMessage;                                                            //签名
        signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signMessage);                           //格式化
        EthSendTransaction sendTransaction = null;
        try {
            sendTransaction = web3j.ethSendRawTransaction(hexValue).send();       //发送交易
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (sendTransaction == null) {
            return "请求网络错误，请检查后重新提交";
        }else {
            String transactionHash = sendTransaction.getTransactionHash();
            if (transactionHash==null){
                Response.Error error = sendTransaction.getError();
                return error.getMessage();
            }else {
                return transactionHash;
            }

        }
    }

//    UUID，生成表里唯一id
    public static String getUUID(){
        String uuid= UUID.randomUUID().toString();
        uuid=uuid.replace("-", "");
        return uuid;
    }

//    助记词和地址获得childECkeyPair的工具类
    public static ECKeyPair getECKeyPairByMnemonic(String mnemonic,String accountPath) throws UnreadableWalletException {
        List<ChildNumber> childNumbers = HDUtils.parsePath(accountPath);
        DeterministicSeed deterministicSeed = new DeterministicSeed(mnemonic, null, "", 0);
        DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();
        BigInteger privKey = deterministicKeyChain.getKeyByPath(childNumbers, true).getPrivKey();
        ECKeyPair childECKeyPair = ECKeyPair.create(privKey);
        return childECKeyPair;
    }
}

